package com.abocide.appium.sdk.factory;

import com.abocide.appium.sdk.core.xml.Node;
import com.abocide.appium.sdk.core.xml.Page;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PageFactory {

    private static final SAXReader reader;

    static {
        reader = new SAXReader();
    }

    public static Page build(InputStream inputStream) {
        try {
            Document document = reader.read(inputStream);
            // 获取根元素
            Element rootElement = document.getRootElement();
            return buildPage((Element) rootElement.elements().get(0), document.asXML());
        } catch (DocumentException e) {
            e.printStackTrace();
            return null;
        }
    }


    public static Page build(String resource) {
        try (InputStream inputStream = new ByteArrayInputStream(resource.getBytes())) {
            Document document = reader.read(inputStream);
            // 获取根元素
            Element rootElement = document.getRootElement();
            return buildPage((Element) rootElement.elements().get(0), document.getText());
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
            return null;
        }
    }


    private static Page buildPage(Element root, String pageSource) {
        Page page = new Page();
        Node node = convertNode(root, page, 0, "");
        page.setRoot(node);
        page.setPageSource(pageSource);
        return page;
    }


    private static Node convertNode(Element element, Page page, Integer depth, String parentKey) {

        Map<String, List<Node>> contentMap = page.getContentMap();
        Map<Integer, List<Node>> depthMaps = page.getDepthMaps();
        Map<String, Node> keyMap = page.getKeyMap();

        Node node = new Node();
        Map<String, String> attrs = new HashMap<>();
        List<Attribute> attributes = element.attributes();
        attributes.stream().forEach(item -> attrs.put(item.getName(), item.getValue()));
        node.setIndex(Integer.valueOf(element.attribute("index").getValue()));
        node.setClassName(element.attribute("class").getValue());
        node.setResourceId(element.attribute("resource-id").getValue());
        node.setPackageName(element.attribute("package").getValue());
        node.setContentDesc(element.attribute("content-desc").getValue().replace("\u200B", ""));
        Attribute bounds = element.attribute("bounds");


        String[] split = bounds.getValue().split(",");
        Node.Location location = new Node.Location();
        location.setLeft(BigDecimal.valueOf(Double.valueOf(split[0].substring(1))));
        location.setTop(BigDecimal.valueOf(Double.valueOf(split[1].substring(0, split[1].indexOf("]")))));
        location.setBottom(BigDecimal.valueOf(Double.valueOf(split[2].substring(0, split[2].length() - 1))));
        location.setRight(BigDecimal.valueOf(Double.valueOf(split[1].substring(split[1].indexOf("[") + 1))));
        node.setLocation(location);
        node.setDepth(depth);
        node.setInstance(Integer.valueOf(element.attribute("instance").getValue()));
        node.setAttributes(attrs);
        node.setParentKey(parentKey);
        node.setKey(depth == 0 ? String.valueOf(node.getIndex()) : parentKey + "_" + node.getIndex());
        keyMap.put(node.getKey(), node);
        List<Node> contents = contentMap.get(node.getContentDesc());
        if (CollectionUtils.isEmpty(contents)) {
            contents = new ArrayList<>();
        }
        contents.add(node);
        contentMap.put(node.getContentDesc(), contents);

        List<Node> depths = depthMaps.get(depth);
        if (CollectionUtils.isEmpty(depths)) {
            depths = new ArrayList<>();
        }
        depths.add(node);
        depthMaps.put(depth, depths);

        List<Element> elements = element.elements();
        if (!CollectionUtils.isEmpty(elements)) {
            List<Node> nodes = new ArrayList<>();
            for (Element ele : elements) {
                Node tempNode = convertNode(ele, page, depth + 1, node.getKey());
                nodes.add(tempNode);
            }
            node.setNodes(nodes);
        }
        return node;
    }


}
